#include "MyCommander.h"

#ifdef _MY_DEBUG
#include "bitmap_image.hpp"
#endif

string MyCommander::getName() const
{
    return "JohnyBot v5";
}


void MyCommander::initialize()
{
    m_LastTime = 0.f;

    m_MapData = new float[m_level->width * m_level->height];
    for (int i = 0; i < m_level->width * m_level->height; ++i)
        m_MapData[i] = m_level->blockHeights[i];
     
    for (auto iter = m_game->bots.begin(); iter != m_game->bots.end(); ++iter)
    {
        const BotInfo& bot = *iter->second;
        m_botAliveState[bot.name] = false;
    }

    for (auto iter = m_game->team->members.begin(); iter != m_game->team->members.end(); ++iter)
    {
        m_MyBotInfo[(*iter)->name] = new MyBotInfo();
    }

    DumpCreate();
    DumpImage();

    m_EnemyPossession = false;
    m_MyPossession = false;
    m_SituationChanged = true;
    m_EnemyBotsAlive = m_game->enemyTeam->members.size();
    m_DefendersRatio = 0.4f;
    m_PathFinder = new PathFinder(&*m_level);
    
    auto spawn_area = m_game->team->botSpawnArea;
    LOGT(("spawnarea:%f, %f, %f, %f\n", spawn_area.first.x, spawn_area.first.y, spawn_area.second.x, spawn_area.second.y));
    LOGT(("flag spawn location: %f, %f\n", m_game->team->flagSpawnLocation.x, m_game->team->flagSpawnLocation.y));

    //MapBuffer buffer(m_level->width, m_level->height, m_MapData);
    //vector<Vector2> ambush_points;
    //CalcAmbushPoint(buffer, m_game->enemyTeam->botSpawnArea.first, m_game->team->flagSpawnLocation, ambush_points);

    //MapBuffer visibility(m_level->width, m_level->height, m_MapData);
    //CalcVisibilityMap(visibility);

    Vector2 tmp_dst = m_game->enemyTeam->flagScoreLocation;
    Path tmp_path;
    m_PathFinder->FindPath(spawn_area.first, tmp_dst, tmp_path);
    DumpPath(tmp_path, 0, 0, 255);

    DumpDestroy("color_map_image.bmp");
    DumpCreate();
}

void MyCommander::tick()
{
    m_DeltaTime = m_game->match->timePassed - m_LastTime;
    m_LastTime = m_game->match->timePassed;
    //vector<Vector2> ambush_points;
    //CalcAmbushPoint(m_game->enemyTeam->botSpawnArea.first, m_game->team->flagSpawnLocation, ambush_points);

    UpdateFacts();

    ReassignBotRoles();
    ////LOG(("Defenders: %d, Assault: %d, Runners: %d, EnemyBotsAlive: %d\n", m_Defenders.size(), m_Assaults.size(), m_Runners.size(), m_EnemyBotsAlive));

    UpdateDefenders();
    UpdateAssaults();
    UpdateRunners();

    for (uint i = 0; i < m_game->team->members.size(); ++i)
    {
        BotInfo* bot = m_game->team->members[i];

        if (bot->health > 0.f)
            assert(m_MyBotInfo[bot->name]->GetGoal() != EGoal::None && m_MyBotInfo[bot->name]->GetAction());
        else
            assert(m_MyBotInfo[bot->name]->GetGoal() == EGoal::None && m_MyBotInfo[bot->name]->GetAction() == NULL);
    }

    for (auto it = m_game->bots_alive.begin(); it != m_game->bots_alive.end(); ++it)
    {
        BotInfo* bot = *it;
        assert(m_MyBotInfo[bot->name]->GetGoal() != EGoal::None);
    }

    for (auto iter = m_game->team->members.begin(); iter != m_game->team->members.end(); ++iter)
    {
        const BotInfo& bot = **iter;
        assert(m_botAliveState[bot.name] == *bot.health > 0);
    }
}

void MyCommander::shutdown()
{
    delete [] m_MapData;
    delete m_PathFinder;

    for (auto iter = m_game->team->members.begin(); iter != m_game->team->members.end(); ++iter)
        delete m_MyBotInfo[(*iter)->name];
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::ReassignBotRoles()
{
    if (!m_SituationChanged)
        return;

    m_SituationChanged = false;

    vector<BotInfo*> bots_available = m_game->bots_alive;
    if (bots_available.size() == 0)
        return;

    if (m_MyPossession) // i'm in possession of enemy flag
    {
        BotInfo* bot_with_flag = NULL;
        auto it = bots_available.begin();
        for (; it != bots_available.end(); ++it)
        {
            BotInfo* bot = *it;
            if (bot->health > 0.0f && bot->flag)
            {
                bot_with_flag = bot;
                break;
            }
        }

        m_MyBotInfo[bot_with_flag->name]->SetGoal(EGoal::ReturnFlag);
        bots_available.erase(it);
    }

    if (m_EnemyBotsAlive == 0 && bots_available.size() > 0)
    {
        Vector2 enemy_flag = m_game->enemyTeam->flag->position;
        uint index = FindClosestBot(bots_available, enemy_flag);

        m_MyBotInfo[bots_available[index]->name]->SetGoal(EGoal::ReturnFlag);

        auto it = bots_available.begin();
        advance(it, index);
        bots_available.erase(it);
    }

    //if (m_EnemyPossession) // enemy in possession of my flag, all available bots become assaults 
    //{
    //    for (uint i = 0; i < bots_available.size(); ++i)
    //        m_MyBotInfo[bots_available[i]->name]->SetGoal(EGoal::Attack);

    //    return;
    //}

    //if (!m_EnemyPossession) // my flag in my possession
    {
        uint num_defenders = GetOptimalNumDefenders();

        if (num_defenders >= bots_available.size())
        {
            for (uint i = 0; i < bots_available.size(); ++i)
                m_MyBotInfo[bots_available[i]->name]->SetGoal(EGoal::Defend);

            return;
        }

        // find num_defenders bots closest to the flag
        for (uint i = 0; i < num_defenders; ++i)
        {
            Vector2 my_flag_pos = m_game->team->flag->position;

            uint index = FindClosestBot(bots_available, my_flag_pos);

            m_MyBotInfo[bots_available[index]->name]->SetGoal(EGoal::Defend);
            auto it = bots_available.begin();
            advance(it, index);
            bots_available.erase(it);
        }
    }

    for (uint i = 0; i < bots_available.size(); ++i)
        m_MyBotInfo[bots_available[i]->name]->SetGoal(EGoal::Attack);


    int num_attackers = 0;
    int num_defenders = 0;
    int num_runners = 0;
    for (auto it = m_MyBotInfo.begin(); it != m_MyBotInfo.end(); ++it)
    {
        MyBotInfo* my_bot_info = it->second;

        if (my_bot_info->GetGoal() == EGoal::Attack)
            num_attackers++;
        else if (my_bot_info->GetGoal() == EGoal::Defend)
            num_defenders++;
        else if (my_bot_info->GetGoal() == EGoal::ReturnFlag)
            num_runners++;
    }
    LOGT(("ReassignBotRoles defenders: %d, assaults: %d, runners: %d\n", num_defenders, num_attackers, num_runners));
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::UpdateFacts()
{
    bool res = HandleMatchEvents();
    if (res)
        LOGT(("my possession: %d, enemy possession: %d, enemies alive: %d (from events)\n", m_MyPossession, m_EnemyPossession, m_EnemyBotsAlive));

    m_SituationChanged = res;

    vector<string> visible_bots;

    for (uint i = 0; i < m_game->team->members.size(); ++i)
    {
        BotInfo* bot_info = m_game->team->members[i];
        for (uint j = 0; j < bot_info->seenBy.size(); ++j)
        {
            if (std::find(visible_bots.begin(), visible_bots.end(), bot_info->seenBy[j]->name) == visible_bots.end())
                visible_bots.push_back(bot_info->seenBy[j]->name);
        }
    }

    bool visible_bots_changed = false;
    if (visible_bots.size() != m_VisibleEnemies.size())
        visible_bots_changed = true;
    else
    {
        for (uint i = 0; i < visible_bots.size(); ++i)
            if (std::find(m_VisibleEnemies.begin(), m_VisibleEnemies.end(), visible_bots[i]) != m_VisibleEnemies.end())
                continue;
            else
            {
                visible_bots_changed = true;
                break;
            }       
    }

    if (visible_bots_changed)
    {
        string s;
        for (uint i = 0; i < visible_bots.size(); ++i)
            s += visible_bots[i] + " ";

        //LOGT((" visible enemies %d: %s\n", visible_bots.size(), s.c_str()));

        m_VisibleEnemies.clear();
        for (uint i = 0; i < visible_bots.size(); ++i)
            m_VisibleEnemies.push_back(visible_bots[i]);

        //for (uint i = 0; i < m_game->enemyTeam->members.size(); ++i)
        //{
        //    BotInfo* bot_info = m_game->enemyTeam->members[i];
        //    //LOG((" %s (%s): %.2f ", bot_info->name.c_str(), m_botAliveState[bot_info->name] ? "alive" : "dead", bot_info->seenlast.get_value_or(-1.f)));
        //}
        //LOG(("\n"));
    }

    //m_EnemyFieldOfFire->Clear(m_MapData);

}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::UpdateDefenders()
{
    Vector2 flag_position = m_game->team->flag->position;

    for (auto it = m_MyBotInfo.begin(); it != m_MyBotInfo.end(); ++it)
    {
        BotInfo* bot_info = &*m_game->bots[it->first];
        MyBotInfo* my_bot_info = it->second;
        Action* action = my_bot_info->GetAction();

        if (my_bot_info->GetGoal() != EGoal::Defend)
            continue;

        if (action && action->GetResult() == EResult::InProgress)
        {
            action->Update(m_DeltaTime);
        }
        else
        {
            if (bot_info->position->distance(flag_position) > 5.f)
                SetAction(bot_info->name, new Move(this, bot_info->name, "def", flag_position));
            else
                SetAction(bot_info->name, new DefensiveMove(this, bot_info->name, "def", flag_position));
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::UpdateAssaults()
{
    Vector2 enemy_flag_pos = m_game->enemyTeam->flag->position;

    Vector2 random_pos;
    Vector2 min = enemy_flag_pos - 20.f;
    Vector2 max = enemy_flag_pos + 20.f;
    m_level->findRandomFreePositionInBox(random_pos, min, max);


    for (auto it = m_MyBotInfo.begin(); it != m_MyBotInfo.end(); ++it)
    {
        BotInfo* bot_info = &*m_game->bots[it->first];
        MyBotInfo* my_bot_info = it->second;
        Action* action = my_bot_info->GetAction();

        if (my_bot_info->GetGoal() != EGoal::Attack)
            continue;

        if (action && action->GetResult() == EResult::InProgress)
        {
            action->Update(m_DeltaTime);
        }
        else
        {
            if (Utils::IsLOS(&*m_level, *bot_info->position, enemy_flag_pos) && !SeeDanger(bot_info))
                SetAction(bot_info->name, new Move(this, bot_info->name, "as", enemy_flag_pos));
            else
                SetAction(bot_info->name, new Move(this, bot_info->name, "as", random_pos));
        }

    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::UpdateRunners()
{
    Vector2 enemy_flag_pos = m_game->enemyTeam->flag->position;
    Vector2 score_location = m_game->team->flagScoreLocation;


    for (auto it = m_MyBotInfo.begin(); it != m_MyBotInfo.end(); ++it)
    {
        BotInfo* bot_info = &*m_game->bots[it->first];
        MyBotInfo* my_bot_info = it->second;
        Action* action = my_bot_info->GetAction();

        if (my_bot_info->GetGoal() != EGoal::ReturnFlag)
            continue;

        if (action && EAction::IsMoveAction(action->GetType()))
        {
            MoveAction* sm = static_cast<MoveAction*>(action);
            Vector2 dest = sm->GetDest();

            if (bot_info->flag && dest.distance(score_location) < 1.f || // if got flag and running to score location or
                !bot_info->flag && dest.distance(enemy_flag_pos) < 1.f) // if does not have the flag and runs to it
            {
                action->Update(m_DeltaTime);

                if (action->GetResult() == EResult::InProgress)
                    continue;
            }
        }

        //if (!action || action->GetResult() != EResult::InProgress)
        if (bot_info->flag)
            SetAction(bot_info->name, new Move(this, bot_info->name, "return flag", score_location, true));
        else
            SetAction(bot_info->name, new Move(this, bot_info->name, "get flag", enemy_flag_pos, true));

    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
bool MyCommander::HandleMatchEvents()
{
    bool res = false;
    std::vector<MatchCombatEvent>& combat_events = m_game->match->combatEvents;

    for (auto it = combat_events.begin(); it != combat_events.end(); ++it)
    {
        res = true;

        switch (it->type)
        {
        case MatchCombatEvent::TYPE_FLAG_PICKEDUP:
            if(it->flagPickupEventData.subject == m_game->enemyTeam->flag)
            {
                LOGT(("enemy flag pickedup (%f)\n", it->time));
                m_MyPossession = true;
            }
            else if (it->flagPickupEventData.subject == m_game->team->flag)
            {
                LOGT(("my flag pickedup by %s (%f)\n", it->flagPickupEventData.instigator->name.c_str(), it->time));
                m_EnemyPossession = true;
            }
            break;
             
        case MatchCombatEvent::TYPE_FLAG_DROPPED:
            if(it->flagDroppedEventData.subject == m_game->enemyTeam->flag)
            {
                LOGT(("enemy flag dropped (%f)\n", it->time));
                m_MyPossession = false;
            }
            else if (it->flagDroppedEventData.subject == m_game->team->flag)
            {
                LOGT(("my flag dropped by %s (%f)\n", it->flagDroppedEventData.instigator->name.c_str(), it->time));
                m_EnemyPossession = false;
            }
            break;

        case MatchCombatEvent::TYPE_FLAG_CAPTURED:
            if(it->flagCapturedEventData.subject == m_game->enemyTeam->flag)
            {
                LOGT(("enemy flag captured (%f)\n", it->time));
                m_MyPossession = false;
            }
            else if (it->flagCapturedEventData.subject == m_game->team->flag)
            {
                LOGT(("my flag captured by %s (%f)\n", it->flagCapturedEventData.instigator->name.c_str(), it->time));
                m_EnemyPossession = false;
            }
            break;

        case MatchCombatEvent::TYPE_FLAG_RESTORED:
            if(it->flagRestoredEventData.subject == m_game->enemyTeam->flag)
            {
                LOGT(("enemy flag restored (%f)\n", it->time));
                m_MyPossession = false;
            }
            else if (it->flagRestoredEventData.subject == m_game->team->flag)
            {
                LOGT(("my flag restored (%f)\n", it->time));
                m_EnemyPossession = false;
            }
            break;

        case MatchCombatEvent::TYPE_KILLED:
            //LOGT(("%s killed by %s (%f)\n", it->killedEventData.subject->name.c_str(), it->killedEventData.instigator->name.c_str(), it->time));

            {
                BotInfo* bot_info = it->killedEventData.subject;
                if (bot_info->team == m_game->team) // bot from my team
                {
                    m_MyBotInfo[it->killedEventData.subject->name]->Clear();
                }
            }

            if (it->killedEventData.subject->team == m_game->enemyTeam)
                m_EnemyBotsAlive = max(0u, m_EnemyBotsAlive - 1);

            if (it->killedEventData.subject->flag)
            {
                if (it->killedEventData.subject->team == m_game->enemyTeam)
                {
                    LOGT(("killed enemy flag holder (%f)\n", it->time));
                    m_EnemyPossession = false;
                }
                else if (it->killedEventData.subject->team == m_game->team)
                {
                    LOGT(("killed my flag holder (%f)\n", it->time));
                    m_MyPossession = false;
                }
            }

            m_botAliveState[it->killedEventData.subject->name] = false;
            break;

        case MatchCombatEvent::TYPE_RESPAWN:
            //LOGT(("respawn\n"));
            m_EnemyBotsAlive = m_game->enemyTeam->members.size();

            m_botAliveState[it->respawnEventData.subject->name] = true;
            break;
        }
    }

    combat_events.clear(); // TODO is it all right?
    return res;
} 

///////////////////////////////////////////////////////////////////////////////////////////////////
uint MyCommander::FindClosestBot(vector<BotInfo*> bots, const Vector2& pos)
{
    assert(bots.size() > 0);

    int closest_index = 0;
    float min_dist = bots[0]->position->squaredDistance(pos);

    for (uint i = 1; i < bots.size(); ++i)
    {
        float dist = bots[i]->position->squaredDistance(pos);
        if (dist < min_dist)
        {
            min_dist = dist;
            closest_index = i;
        }
    }

    assert(closest_index < (int) bots.size());
    return closest_index;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
uint MyCommander::GetOptimalNumDefenders() 
{
    return (uint) (m_DefendersRatio * (float) m_game->team->members.size()); //bots_available.size(); // try to keep the same numer of defenders even after losing bots 
}

///////////////////////////////////////////////////////////////////////////////////////////////////
bool MyCommander::SeeDanger(BotInfo* bot)
{
    for (uint i = 0; i < bot->seenBy.size(); ++i)
    {
        BotInfo* bot_seen = bot->seenBy[i];
        Vector2 d = *m_game->bots[bot_seen->name]->facingDirection;
        if (m_game->bots[bot_seen->name]->facingDirection->dotProduct(m_game->enemyTeam->flag->position - *bot->position) < -0.5f)
        {
            return true;
        }
    }
    return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::DumpCreate()
{
#ifdef _MY_DEBUG
    m_DebugImage = new bitmap_image(m_level->width, m_level->height);
#endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::DumpDestroy(string name)
{
#ifdef _MY_DEBUG
    m_DebugImage->save_image(name.c_str());
    delete m_DebugImage;
#endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::DumpPath(const Path& path, uchar r, uchar g, uchar b)
{
#ifdef _MY_DEBUG
    for (uint i = 0; i < path.size(); ++i)
        m_DebugImage->set_pixel((uint) path[i].x, (uint) path[i].y, r, g, b);
#endif // _MY_DEBUG
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::DumpImage()
{
#ifdef _MY_DEBUG
    for (int x = 0; x < m_level->width; ++x)
    {
        for (int y = 0; y < m_level->height; ++y)
        {
            uchar col = m_level->blockHeights[x + y * m_level->width] > 1 ? 0 : 255;

            m_DebugImage->set_pixel(x, y, col, col, col);
        }
    }
#endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::DumpBuffer(const MapBuffer& buffer, const string& file_name, float v1, float v2)
{
#ifdef _MY_DEBUG
    bitmap_image* image = new bitmap_image(m_level->width, m_level->height);

    for (int x = 0; x < m_level->width; ++x)
    {
        for (int y = 0; y < m_level->height; ++y)
        {
            uchar col = 0;
            
            float v = buffer.Get(x, y);
            if (v >= 2.f)
                col = 0;
            else if (v >= 1.f)
                col = 80;
            else if (v >= v2)
                col = 160;
            else if (v >= v1)
                col = 200;
            else
                col = 255;

            image->set_pixel(x, y, col, col, col);
        }
    }

    image->save_image(file_name.c_str());
    delete image;
#endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::DumpBufferGradient(const MapBuffer& buffer, const string& file_name)
{
#ifdef _MY_DEBUG
    bitmap_image* image = new bitmap_image(m_level->width, m_level->height);

    float max_value = -FLT_MAX;
    for (int x = 0; x < m_level->width; ++x)
    {
        for (int y = 0; y < m_level->height; ++y)
        {
            if (buffer.Get(x, y) >= 1.f)
                continue;
            if (buffer.Get(x, y) > max_value)
                max_value = buffer.Get(x, y);
        }
    }


    for (int x = 0; x < m_level->width; ++x)
    {
        for (int y = 0; y < m_level->height; ++y)
        {
            float v = buffer.Get(x, y);
            if (v >= 1.f)
            {
                if (v >= 2.f)
                    image->set_pixel(x, y, 100, 0, 0);
                else
                    image->set_pixel(x, y, 170, 0, 0);
            }
            else
            {
                uchar c = (uchar) ((v / max_value) * 255.f);
                image->set_pixel(x, y, c, c, c);
            }
        }
    }

    image->save_image(file_name.c_str());
    delete image;
#endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::CalcAmbushPoint(MapBuffer& buffer, Vector2 src, Vector2 dest, vector<Vector2> ambush_points)
{
    long start_time = clock();
    Path path;
    // dest replace with src, so that points close to dest would be at the beginning of the path
    m_PathFinder->FindPath(dest, src, path, false, true);
    DumpPath(path, 128, 0, 0);

    // we are looking for points "close" to dest
    //int path_size = (int) (path.size() * 0.75f);
    //for (int i = path.size() - 1; i >= path_size; --i)
    //{
    //    path.pop_back();
    //}

    LOGT(("Path len: %d\n", path.size()));

    assert(!path.empty());

    long after_path_time = clock();

    // 10% safety margin added to fov
    float fov = 1.1f * m_level->fieldOfViewAngles[BotInfo::STATE_ATTACKING];

    // 10% safety margin added to firing distance
    float firing_dist = m_level->firingDistance * 1.1f;
    float max_dist_from_path = /*firing_dist * 0.5f;*/sin(fov * 0.5f) * firing_dist;
    
    SelectFOV(buffer, path[path.size() - 1], path[path.size() - 2], 2.f * PI, firing_dist, 0.5f);
    for (uint i = 0; i < path.size() - 2; ++i)
    {
        SelectFOV(buffer, path[i + 1], path[i] - path[i + 1], fov, firing_dist * 3.f, 0.5f); // bigger firing_dist to remove points that are seen from previous path segments
    }

    long after_visible_time = clock();

    for (int iy = 0; iy < m_level->height; ++iy)
    {

        for (int ix = 0; ix < m_level->width; ++ix)
        {
            Vector2 p(ix + 0.5f, iy + 0.5f);
            int p_ok = 0;//bool p_ok = false;

            if (m_level->blockHeights[ix + iy * m_level->width] || buffer.Get(ix, iy) == 0.5f)
                continue;

            for (uint i = 0; i < path.size(); ++i)
            {
                if (p.distance(path[i]) < max_dist_from_path && Utils::IsLOS(&*m_level, p, path[i]))
                {
                    p_ok++;
                    if (p_ok == 3)
                        break;
                }
            }

            if (p_ok == 3)
            {
                buffer.Set(ix, iy, 0.6f);
            }
        }
    }

    DumpBuffer(buffer, "ambush_points.bmp", 0.5f, 0.6f);

    LOGT(("path find and shortening time:%d\n", after_path_time - start_time));
    LOGT(("visibility time:%d\n", after_visible_time - after_path_time));
    LOGT(("selecting fov time:%d\n", clock() - after_visible_time));
    LOGT(("total time:%d\n", clock() - start_time));

    DumpPath(path, 255, 0, 0);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::SelectFOV(MapBuffer& buffer, Vector2 p, Vector2 dir, float angle, float length, float selection_value)
{
    dir.normalise();
    float cos_angle = cosf(angle * 0.5f);
    

    for (int iy = (int) max((int) floorf(p.y - length), 0); iy < min((int) ceilf(p.y + length), m_level->height); ++iy)
    {

        for (int ix = (int) max((int) floorf(p.x - length), 0); ix < min((int) ceilf(p.x + length), m_level->width); ++ix)
        {
            Vector2 pp(ix + 0.5f, iy + 0.5f);
            if (m_level->blockHeights[ix + iy * m_level->width])
                continue;

            if (pp.distance(p) > length)
                continue;

            Vector2 this_dir = pp - p;
            this_dir.normalise();
            if (this_dir.dotProduct(dir) < cos_angle)
                continue;  
            //m_DebugImage->set_pixel(ix, iy, 100, 100, 200);
            
            if (Utils::IsLOS(&*m_level, p, pp))
            {
                buffer.Set(ix, iy, selection_value);
            }
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyCommander::CalcVisibilityMap(MapBuffer& buffer)
{
    long start_time = clock();

    for (int iy = 0; iy < m_level->height; ++iy)
    {
        for (int ix = 0; ix < m_level->width; ++ix)
        {
            if (buffer.Get(ix, iy) >= 1.f)
                continue;

            Vector2 p(ix + 0.5f, iy + 0.5f);
            int num = 0;

            for (int iyy = 0; iyy < m_level->height; ++iyy)
            {
                for (int ixx = 0; ixx < m_level->width; ++ixx)
                {
                    Vector2 pp(ixx + 0.5f, iyy + 0.5f);

                    if (Utils::IsLOS(&*m_level, p, pp))
                        num++;
                }
            }

            buffer.Set(ix, iy, (float) num / (float) (m_level->height * m_level->width));
        }
    }

    LOGT(("CalcVisibilityMap time:%d\n", clock() - start_time));

    DumpBufferGradient(buffer, "visibility_map.bmp");
}


///////////////////////////////////////////////////////////////////////////////////////////////////
MyBotInfo::MyBotInfo()
{
    Clear();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
MyBotInfo::~MyBotInfo()
{
    delete m_Action;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyBotInfo::Clear()
{
    m_Action = NULL; m_Goal = EGoal::None;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyBotInfo::Set(EGoal::Type goal, Action* a)
{
    SetGoal(goal);
    SetAction(a);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyBotInfo::SetAction(Action* a)
{
    m_Action = a;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void MyBotInfo::SetGoal(EGoal::Type goal)
{
    if (goal != m_Goal)
        m_Action = NULL;

    m_Goal = goal;
}
